/*  ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** 
*   Copyright 2014 Jan-Marwin Wollschläger                                   *
*                                                                            *
*   Licensed under the Apache License, Version 2.0 (the "License");          *
*   you may not use this file except in compliance with the License.         *
*   You may obtain a copy of the License at                                  *
*                                                                            *
*       http://www.apache.org/licenses/LICENSE-2.0                           *
*                                                                            *
*   Unless required by applicable law or agreed to in writing, software      *
*   distributed under the License is distributed on an "AS IS" BASIS,        *
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
*   See the License for the specific language governing permissions and      *
*   limitations under the License.                                           *
** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **   */   
import java.util.ArrayList;

/**
 * Basic implementation of a Molecule.
 *
 * @author Jan-Marwin Wollschläger
 * @version 0.1
 **/
public class Molecule implements IMolecule
{
    private ArrayList<IAtom> atoms;
    private ArrayList<IBond> bonds;
    
    public Molecule(ArrayList<IAtom> atoms, ArrayList<IBond> bonds)
    {
       this.atoms = atoms; this.bonds = bonds;    
    } 
    
    public Molecule()
    {
       this.atoms = new ArrayList<IAtom>();
       this.bonds = new ArrayList<IBond>(); 
    }
    
    
   /**
   * @return Returns the atoms contained in this molecule as ArrayList of IAtom instances.
   **/
   public ArrayList<IAtom> getAtoms(){ return atoms; }
  
   /**
   * @return Returns a list of all bonds contained in this molecule as ArrayList of IBond
   * instances.
   **/
   public ArrayList<IBond> getBonds(){ return bonds; }
   
   
   public IAtom getAtom(int atomIndex){ return atoms.get(atomIndex); }
   
   public IBond getBond(int bondIndex){ return bonds.get(bondIndex); }
  
   /**
   * @param atoms The IAtom instances - e.g. atoms - of this molecule.
   **/
   public void setAtoms(ArrayList<IAtom> atoms){ this.atoms = atoms; }
   
   /**
   * @param bonds The Bonds for this molecule instance that link the atoms with one another.
   **/
   public void setBonds(ArrayList<IBond> bonds){ this.bonds = bonds; }
   
   /**
   *  @param atomToAdd The atom to add to the list of atoms contained in this molecule. 
   **/
   public void addAtom(IAtom atomToAdd){ this.atoms.add(atomToAdd); }
  
   /**
   * @param bondToAdd The bond to add to the list of bonds contained in this molecule.
   **/   
   public void addBond(IBond bondToAdd){ this.bonds.add(bondToAdd); }
   
   
   /**
   * Joins (or connects) two molecules at the specified atoms. It is
   * possible that fstMolecule == sndMolecule. In this case, just a bond
   * is added connecting the atoms fstAtom and sndAtom. If on the other
   * hand fstMolecule != sndMolecule, so the two specified molecules
   * differ from one another, both molecules will be joined to a new
   * IMolecule which will be the result of this method call.
   * @param fstAtom The atom contained in fstMolecule to perform the
   * joining operation at.
   * @param sndAtom The atom contained in sndMolecule where to perform
   * the joining operation at.
   * @param fstMolecule The first molecule to join with the second.
   * @param sndMolecule The second molecule to join with the first.
   * @return The joined molecule will be returned as an IMolecule
   * instance. 
   **/
   public IMolecule join(IAtom fstAtom, IAtom sndAtom,  
						   IMolecule fstMolecule,  
						   IMolecule sndMolecule)
   {
	   if(fstAtom == sndAtom)
	   {
		   if(fstMolecule != sndMolecule)
			System.err.println("It is not possible to join the same"+
			" atoms in different molecules. Check the client code!");
			
			return fstMolecule;  // Joining an atom in a molecule with
								  // itself just does nothing!	
	   }
	   
	   if(fstMolecule == sndMolecule)
	   {
		   fstMolecule.addBond(new Bond(fstAtom, sndAtom));
		   return fstMolecule;
	   }
	   
	   IMolecule joinedMolecule = new Molecule();
	   for(IAtom atom : fstMolecule.getAtoms())
			joinedMolecule.addAtom(atom);
	   for(IBond bond : fstMolecule.getBonds())
			joinedMolecule.addBond(bond);
	   for(IAtom atom : sndMolecule.getAtoms())
			joinedMolecule.addAtom(atom);
	   for(IBond bond : sndMolecule.getBonds())
			joinedMolecule.addBond(bond);
	   joinedMolecule.addBond(new Bond(fstAtom, sndAtom));
	   return joinedMolecule;
   }
   
   
   /**
   * This method returns the atoms of this molecule instance that are 
   * adjacent to the specified IAtom instance.
   * @param atom The atom to get the adjacent atoms of.
   * @return The list of adjacent atoms as ArrayList<IAtom>. 
   **/
   public ArrayList<IAtom> getAdjacentAtoms(IAtom atom)
   {
	   ArrayList<IAtom> adjAtms = new ArrayList<IAtom>();	
	   for(IBond bnd : bonds)
	   {
		   if(bnd.getFirstAtom()==atom)
		   {
			   final IAtom adjAtm = bnd.getSecondAtom();
			   if(!adjAtms.contains(adjAtm))
					adjAtms.add(adjAtm);
		   }
		   else
		   {
			  if(bnd.getSecondAtom()==atom)
			  {
			   final IAtom adjAtm = bnd.getFirstAtom();
			   if(!adjAtms.contains(adjAtm))
					adjAtms.add(adjAtm);
		      } 
		   }
	   }
	   return adjAtms;
   }
   
   /**
   * Returns true if the specified atom is part of this molecule,
   * returns false otherwise.
   * @param atom The atom to check.
   * @return True if atom is contained in this instance, false
   * otherwise.
   */
   public boolean containsAtom(IAtom atom)
   {
	   return atoms.contains(atom);
   }
   
   /**
   * Returns true, if this atom label is implicit, e.g. the atom label
   * or 'atom symbol' is not to be displayed. An example for this 
   * are carbon atoms which are generally not explicitly denoted by
   * their respective symbol ('C').
   * 
   * @param whichAtom The atom for which to check if it is implicit.
   **/
   public boolean isAtomImplicit(IAtom whichAtom)
   {
	   return false;
   }
   
   
   /**
   * Computes the number of atoms of this instance.
   * @return The number of atoms of this instance as an integer.
   */
   public int getAtomCount()
   {
	   return atoms.size();
   }
   
   
   /**
   * Computes the number of bonds of this instance.
   * @return The number of bonds of this instance as an integer.
   */
   public int getBondCount()
   {
	   return bonds.size();
   }
   
   
}


